package cn.itcast.nems.order.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.itcast.ic.common.bean.Page;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.ic.common.exception.v2.bean.BizErrorBodyEnum;
import cn.itcast.nems.base.api.BusinessLineApi;
import cn.itcast.nems.base.api.OriginChannelApi;
import cn.itcast.nems.base.api.PaymentTypeApi;
import cn.itcast.nems.base.dto.BusinessLineDTO;
import cn.itcast.nems.base.dto.OriginChannelDTO;
import cn.itcast.nems.base.dto.PaymentTypeDTO;
import cn.itcast.nems.common.account.AccountHolder;
import cn.itcast.nems.common.constant.*;
import cn.itcast.nems.common.enumeration.BizNumberTypeEnum;
import cn.itcast.nems.common.enumeration.NemsErrorBodyEnum;
import cn.itcast.nems.common.service.BizNumberService;
import cn.itcast.nems.common.tenant.TenantInfoHolder;
import cn.itcast.nems.common.utils.WrapperUtil;
import cn.itcast.nems.common.validator.CustomerValidator;
import cn.itcast.nems.discount.api.DiscountApi;
import cn.itcast.nems.discount.dto.DiscountSummaryDTO;
import cn.itcast.nems.journal.service.JournalRenewalOrderRelationshipService;
import cn.itcast.nems.journal.service.JournalTransferOrderRelationshipService;
import cn.itcast.nems.manager.enrol.EnrolManager;
import cn.itcast.nems.manager.rocketmq.MessageManager;
import cn.itcast.nems.manager.rocketmq.constant.MessageTagConstant;
import cn.itcast.nems.manager.util.CustomBeanUtil;
import cn.itcast.nems.order.api.JournalDiscountApi;
import cn.itcast.nems.order.dao.dataobject.OrderDO;
import cn.itcast.nems.order.dao.dataobject.StudentClazzIdDO;
import cn.itcast.nems.order.dao.entity.*;
import cn.itcast.nems.order.dao.mapper.OrderMapper;
import cn.itcast.nems.order.dao.mapper.OrderPaymentLinkMapper;
import cn.itcast.nems.order.dto.*;
import cn.itcast.nems.order.dto.message.OrderCanceledMessageDTO;
import cn.itcast.nems.order.dto.message.OrderCreatedMessageDTO;
import cn.itcast.nems.order.dto.message.OrderPaidMessageDTO;
import cn.itcast.nems.order.enumeration.OrderErrorBodyEnum;
import cn.itcast.nems.order.service.*;
import cn.itcast.nems.product.api.ProductApi;
import cn.itcast.nems.product.dao.entity.ProductPriceRule;
import cn.itcast.nems.product.dto.ProductDTO;
import cn.itcast.nems.product.service.ProductPriceRuleService;
import cn.itcast.nems.product.service.ProductService;
import cn.itcast.nems.product.service.ProductSnapshotService;
import cn.itcast.nems.studentrecord.constant.OrderSourceEnum;
import cn.itcast.nems.studentrecord.dao.entity.JournalRenewalOrderRelationship;
import cn.itcast.nems.studentrecord.dao.entity.JournalRepeat;
import cn.itcast.nems.studentrecord.dao.entity.JournalTransferOrderRelationship;
import cn.itcast.nems.studentrecord.dao.mapper.JournalRepeatMapper;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.javaparser.quality.NotNull;
import com.google.common.base.Joiner;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 销售订单-头 服务实现类
 * </p>
 *
 * @author liyong
 * @since 2023-08-31
 */
@Slf4j
@Service
@AllArgsConstructor
public class OrderServiceImpl implements OrderService {
    private final OrderMapper orderMapper;
    private final OrderExtendService orderExtendService;
    private final OrderEventService orderEventService;
    private final OrderLineService orderLineService;
    private final OrderCustomerRelationshipService orderCustomerRelationshipService;
    private final OrderLineExtendService orderLineExtendService;
    private final OrderDiscountService orderDiscountService;
    private final OrderPaymentFlowService orderPaymentFlowService;
    private final PaymentReceiptService paymentReceiptService;
    private final ProductApi productApi;
    private final ProductService productService;
    private final ProductPriceRuleService productPriceRuleService;
    private final ProductSnapshotService productSnapshotService;
    private final DiscountApi discountApi;
    private final OrderPaymentLinkMapper orderPaymentLinkMapper;
    private final PaymentTypeApi paymentTypeApi;
    private final BusinessLineApi businessLineApi;
    private final OriginChannelApi originChannelApi;
    private final JournalRenewalOrderRelationshipService journalRenewalOrderRelationshipService;
    private final JournalTransferOrderRelationshipService journalTransferOrderRelationshipService;
    private final JournalRepeatMapper journalRepeatMapper;
    /**
     * 订单创建投递消息模版
     */
    private final RocketMQTemplate rocketMQTemplate;
    /**
     * rocketmq  配置参数
     */
    public final RocketMQProperties rocketMQProperties;

    private final BizNumberService bizNumberService;

    private final JournalDiscountApi journalDiscountApi;

    private final EnrolManager enrolManager;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateOrderResultDTO create(@NonNull NewOrderDTO order) {
        log.info("[创建订单接口] 请求表单信息: {}", order);
        // log.info("[创建订单接口] 请求表单信息: {}",  JSON.toJSON(order));
        // 校验参数
        List<NewOrderLineDTO> lines = order.getLines();
        Assert.notEmpty(lines, BizExceptionProducer.produce(OrderErrorBodyEnum.ORDER_LINE_NOT_FOUND));

        NewOrderCustomerRelationshipDTO customer = order.getOrderCustomerRelationship();
        log.info("[创建订单接口] 请求表单客户信息: {}", JSON.toJSON(customer));
        // 验证客户信息
        this.validateCustomerRelationshipNotNull(customer);
        // 检查bizOrderNo是否主租户内唯一
        final String bizOrderNo = order.getBizOrderNo();
        Order bizNoOrder = orderMapper.selectByBizOrderNo(bizOrderNo);
        log.info("[创建订单接口] 根据业务订单号查询订单信息: {}", JSON.toJSON(bizNoOrder));
        Assert.isNull(bizNoOrder, BizExceptionProducer.produce(OrderErrorBodyEnum.ORDER_BIZ_NO_CONFLICT, bizOrderNo));
        /** 提取并转换订单头信息 */
        Order orderEntity = wrapOrderEntity(order);
        log.info("[创建订单接口] 业务订单号: {} , 需要持久化的订单头信息: {}", bizOrderNo, JSON.toJSON(orderEntity));
        final String orderId = orderEntity.getId();

        List<OrderLine> orderLineEntities = new ArrayList<>();
        List<OrderLineExtend> orderLineExtendEntities = new ArrayList<>();
        List<OrderDiscount> orderDiscountEntities = new ArrayList<>();

        // 订单头上的合计值
        Integer orderOriginalAmount = 0;
        Integer orderDiscountAmount = 0;
        Integer orderAmountDue = 0;
        // 根据支付方式获取订单价格
        PaymentTypeDTO typeDTO = paymentTypeApi.findById(order.getPaymentTypeId());
        Assert.notNull(typeDTO, BizExceptionProducer.badRequest(BizErrorBodyEnum.NOT_FOUND.name(), "付款方式ID:%s不存在", order.getPaymentTypeId()));
        final String source = order.getSource();
        for (NewOrderLineDTO line : lines) {
            /** 提取并转换订单行信息 */
            OrderLine orderLineEntity = wrapOrderLineEntity(source, orderId, typeDTO, line, order.getIsValidatePrice());
            orderLineEntities.add(orderLineEntity);
            /** 提取并转换订单行扩展字段信息 */
            if (!CollectionUtils.isEmpty(line.getExtendAttributes())) {
                orderLineExtendEntities = line.getExtendAttributes().stream().map(attr -> new OrderLineExtend(orderId, orderLineEntity.getId(), attr.getName(), attr.getValue())).collect(Collectors.toList());
                log.info("[创建订单接口] 业务订单号: {} ,订单号: {} ,解析订拓展信息: {}", bizOrderNo, orderId, JSON.toJSON(orderLineExtendEntities));
            }
            orderOriginalAmount += line.getOriginalAmount();
            orderDiscountAmount += line.getDiscountAmount();
            orderAmountDue += line.getAmountDue();

            // 更新商品库存数
            productService.increaseInventorySoldQuantity(StringUtils.hasText(line.getProductId()) ? Long.valueOf(line.getProductId()) : null, line.getQuantity());
        }
        log.info("[创建订单接口] 订单ID:{} , 表单优惠项参数: {}", orderId, JSON.toJSON(order.getDiscounts()));
        log.info("[创建订单接口] 订单ID:{} , 表单优惠单项参数: {}", orderId, JSON.toJSON(order.getPersonalDiscounts()));
        orderDiscountEntities = this.wrapOrderDiscountEntityList(orderId, orderLineEntities, order);
        log.info("[创建订单接口] 订单ID:{} , 处理完毕需要保存的优惠项: {}", orderId, JSON.toJSON(orderDiscountEntities));
        // 检查是否有作用于订单头的优惠项
        List<OrderDiscount> forAllDiscounts = orderDiscountEntities.stream().filter(item -> Objects.isNull(item.getOrderLineId())).toList();
        if (CollectionUtil.isNotEmpty(forAllDiscounts)) {
            for (OrderDiscount orderDiscount : forAllDiscounts) {
                orderDiscountAmount += orderDiscount.getDiscountAmount();
            }
        }
        // 保存订单头
        orderEntity.setOriginalAmount(orderOriginalAmount);
        orderEntity.setDiscountAmount(orderDiscountAmount);
        orderEntity.setAmountDue(orderAmountDue);
        //  创建订单有可能存在支付的情况
        orderEntity.setPaidAmount(Optional.ofNullable(order.getPaidAmount()).orElse(0));
        // 检查应缴金额是否 小于等于 0  ,如是 将订单状态改为 “已支付”
        if (orderAmountDue <= 0) {
            orderEntity.setState(OrderStateEnum.PAID.name());
        }
        orderMapper.insert(orderEntity);

        // 保存订单头扩展字段
        if (!CollectionUtils.isEmpty(order.getExtendAttributes())) {
            List<OrderExtend> orderExtendEntities = order.getExtendAttributes().stream()
                    .map(orderExtend -> new OrderExtend(orderId, orderExtend.getName(), orderExtend.getValue()))
                    .collect(Collectors.toList());
            log.info("[创建订单接口] 业务订单号: {}, 订单ID:{} , 需要保持的订单头拓展字段: {}", bizOrderNo, orderId, JSON.toJSON(orderExtendEntities));
            orderExtendService.saveBatch(orderExtendEntities);
        }
        // 维护订单支付链接
        this.persistOrderPaymentLink(order, orderId);

        // 保存订单客户关系
        OrderCustomerRelationship orderCustomerRelationshipEntity = new OrderCustomerRelationship();
        BeanUtils.copyProperties(customer, orderCustomerRelationshipEntity);
        orderCustomerRelationshipEntity.setOrderId(orderId);
        log.info("[创建订单接口] 业务订单号: {}, 需要保持的客户信息: {}", bizOrderNo, JSON.toJSON(customer));
        orderCustomerRelationshipService.saveWithDesensitized(orderCustomerRelationshipEntity);

        // 保存订单事件
        orderEventService.save(new OrderEvent(orderId, OrderEventTypeEnum.CREATED.getName(), ""));

        // 保存订单行
        orderLineService.saveBatch(orderLineEntities);
        // 保存订单行扩展属性
        if (!CollectionUtils.isEmpty(orderLineExtendEntities)) {
            orderLineExtendService.saveBatch(orderLineExtendEntities);
        }
        // 保存订单优惠
        if (!CollectionUtils.isEmpty(orderDiscountEntities)) {
            orderDiscountService.saveBatch(orderDiscountEntities);
        }

        // TODO 保存学籍流转表

        // 投递创建订单消息
        this.sendMessageAfterCreated(orderEntity, order, typeDTO, order.getJoinClassId());
        final List<String> orderLineIds = orderLineEntities.stream().map(OrderLine::getId).collect(Collectors.toList());
        return new CreateOrderResultDTO(orderEntity.getId(), orderEntity.getOrderNo(), orderLineIds, orderEntity.getState());
    }

    private void sendMessageAfterCreated(Order order, @NonNull NewOrderDTO orderDTO, PaymentTypeDTO paymentTypeDTO,Integer joinClassId) {
        OrderCreatedMessageDTO createdMessageDTO = new OrderCreatedMessageDTO();
        createdMessageDTO.setOrderId(order.getId());
        // 老EMS学籍ID
        createdMessageDTO.setJoinClassId(joinClassId);
        // 订单行ID
        List<String> orderLines = orderLineService.listObjs(new LambdaQueryWrapper<OrderLine>().select(OrderLine::getId).eq(OrderLine::getOrderId, order.getId()).orderByAsc(OrderLine::getCreatedDatetime), Object::toString);
        log.info("【订单支付投递消息】字段订单行数据：{}", JSON.toJSONString(orderLines));
        createdMessageDTO.setOrderLineIds(orderLines);
        createdMessageDTO.setOrderNo(order.getOrderNo());
        createdMessageDTO.setBizOrderNo(order.getBizOrderNo());
        createdMessageDTO.setBizDate(order.getBizDate());
        createdMessageDTO.setCreatedDatetime(LocalDateTime.now());
        createdMessageDTO.setAmountDue(order.getAmountDue());
        createdMessageDTO.setCustomerId(order.getCustomerId());
        // TODO 目前SCRM和院校帮创建订单都属于短训子租户 ,并没有进行子租户拆分
        createdMessageDTO.setTenantId(TenantInfoHolder.getRequiredTenantInfo().tenantId());
        createdMessageDTO.setSubtenantId(TenantInfoHolder.getRequiredTenantInfo().subtenantId());
        createdMessageDTO.setBusinessLineId(order.getBusinessLineId());
        createdMessageDTO.setOriginalAmount(order.getOriginalAmount());
        createdMessageDTO.setDiscountAmount(order.getDiscountAmount());
        createdMessageDTO.setSource(order.getSource());
        // 定向培训(原后付费)要求立即生效学籍
        if (PaymentTypeEum.LASTPAYMENT.name().equals(paymentTypeDTO.getCode())) {
            createdMessageDTO.setEffectImmediately(Boolean.TRUE);
        }
        if (null != orderDTO.getIsJoinClass() && orderDTO.getIsJoinClass()) {
            createdMessageDTO.setTheFreshDate(orderDTO.getTheFreshDate());
        }
        AccountHolder.getAccountInfo().ifPresentOrElse(accountInfo -> createdMessageDTO.setOperatorEmail(accountInfo.getEmail()),
                () -> createdMessageDTO.setOperatorEmail(order.getCreatedBy()));
        MessageManager.asyncSendOrderly(rocketMQTemplate, rocketMQProperties.getConsumer().getTopic() + ":" + MessageTagConstant.TAG_NEMS_ORDER_CREATED, JSON.toJSONString(createdMessageDTO), order.getCustomerId());
    }

    public void validateCustomerRelationshipNotNull(NewOrderCustomerRelationshipDTO customerRelationshipDTO) {
        Assert.notNull(customerRelationshipDTO, BizExceptionProducer.produce(OrderErrorBodyEnum.ORDER_CUSTOMER_RELATIONSHIP_NOT_FOUND));
        Assert.notBlank(customerRelationshipDTO.getCustomerId(), BizExceptionProducer.produce(OrderErrorBodyEnum.ORDER_CUSTOMER_ID_NOT_FOUND));
        Assert.notBlank(customerRelationshipDTO.getCustomerName(), BizExceptionProducer.produce(OrderErrorBodyEnum.ORDER_CUSTOMER_NAME_NOT_FOUND));
    }

    @Override
    public void findAndInvokeBaseData(NewOrderDTO orderDTO, String paymentTypeCode, String businessLineCode, String originChanelCode) {
        // 支付方式
        List<PaymentTypeDTO> paymentTypeList = paymentTypeApi.findByParam(new PaymentTypeDTO(null, paymentTypeCode));
        Assert.isTrue(CollectionUtil.isNotEmpty(paymentTypeList), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "付款方式CODE:%s不存在", paymentTypeCode));
        orderDTO.setPaymentTypeId(paymentTypeList.get(0).getId());
        orderDTO.setPaymentTypeName(paymentTypeList.get(0).getName());
        // 业务线
        List<BusinessLineDTO> businessLineList = businessLineApi.findByParam(new BusinessLineDTO(businessLineCode));
        Assert.isTrue(CollectionUtil.isNotEmpty(businessLineList), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "业务线CODE:%s不存在", businessLineCode));
        orderDTO.setBusinessLineId(businessLineList.get(0).getId());
        orderDTO.setBusinessLineName(businessLineList.get(0).getName());
        // 订单来源
        List<OriginChannelDTO> originChannelList = originChannelApi.findByParam(new OriginChannelDTO(originChanelCode));
        Assert.isTrue(CollectionUtil.isNotEmpty(originChannelList), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "订单来源CODE:%s不存在", originChanelCode));
        orderDTO.setOriginChannelId(originChannelList.get(0).getId());
        orderDTO.setOriginChannelName(originChannelList.get(0).getName());
    }

    /**
     * 持久化  订单链接关系表
     *
     * @param line
     */
    private void validateOrderLineNotNull(NewOrderLineDTO line) {
        Assert.isTrue(Objects.nonNull(line.getProductId()) || Objects.nonNull(line.getProductBizNo()), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品ID不能为空！"));
        Assert.notNull(line.getPrice(), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品[%s]价格不能为空！", line.getPrice()));
        Assert.notNull(line.getQuantity(), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品[%s]购买数量不能为空！", line.getQuantity()));
        Assert.notNull(line.getOriginalAmount(), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品[%s]购买金额不能为空！", line.getOriginalAmount()));
        Assert.notNull(line.getDiscountAmount(), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品[%s]优惠金额不能为空！", line.getDiscountAmount()));
        Assert.notNull(line.getAmountDue(), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品[%s]实际应付金额不能为空！", line.getAmountDue()));
    }

    private Order wrapOrderEntity(@NonNull NewOrderDTO order) {
        Order orderEntity = new Order();
        orderEntity.setId(generateSimpleUUID());
        orderEntity.setCustomerId(order.getOrderCustomerRelationship().getCustomerId());
        orderEntity.setOrderNo(this.bizNumberService.next(BizNumberTypeEnum.ORDER));
        orderEntity.setBizOrderNo(order.getBizOrderNo());

        orderEntity.setOriginChannelId(order.getOriginChannelId());
        orderEntity.setOriginChannelName(order.getOriginChannelName());
        orderEntity.setAccountId(order.getAccountId());
        orderEntity.setBizDate(order.getBizDate());
        orderEntity.setCurrency(order.getCurrency());

        orderEntity.setPaymentTypeId(order.getPaymentTypeId());
        orderEntity.setPaymentTypeName(order.getPaymentTypeName());
        orderEntity.setState(OrderStateEnum.PENDING.toString());
        orderEntity.setAssociatedOrderId(order.getAssociatedOrderId());

        orderEntity.setBusinessLineId(order.getBusinessLineId());
        orderEntity.setBusinessLineName(order.getBusinessLineName());
        orderEntity.setCreatedBy(order.getCreatedBy());
        orderEntity.setModifiedBy(order.getCreatedBy());
        orderEntity.setSource(order.getSource());
        return orderEntity;
    }

    private void persistOrderPaymentLink(NewOrderDTO order, String orderId) {
        if (StringUtils.hasText(order.getPayUrl())) {
            OrderEnrollmentLink link = new OrderEnrollmentLink();
            link.setState((byte) 1);
            link.setOrderId(orderId);
            link.setUrl(order.getPayUrl());
            link.setCreatedBy(order.getCreatedBy());
            link.setModifiedBy(order.getCreatedBy());
            orderPaymentLinkMapper.insert(link);
        }
    }

    private OrderLine wrapOrderLineEntity(String orderSource, String orderId, PaymentTypeDTO typeDTO,
                                          NewOrderLineDTO line ,Boolean isValidatePrice) {
        validateOrderLineNotNull(line);

        ProductDTO product = productApi.findProductByIdOrBizNo(StringUtils.hasText(line.getProductId()) ? Long.valueOf(line.getProductId()) : null, line.getProductBizNo());
        Assert.notNull(product, BizExceptionProducer.badRequest(BizErrorBodyEnum.NOT_FOUND.name(), "产品ID[%s]或产品业务编码[%s]不正确，找不到对应的产品。", line.getProductId(), line.getProductBizNo()));

        Long productId = product.getId();
        String latestProductSnapshotId = productSnapshotService.getLatestProductSnapshotId(productId);
        Assert.notBlank(latestProductSnapshotId, BizExceptionProducer.badRequest(BizErrorBodyEnum.NOT_FOUND.name(), "找不到产品[%s]的快照信息。", productId));

        String orderLineId = generateSimpleUUID();
        OrderLine orderLineEntity = new OrderLine();
        orderLineEntity.setId(orderLineId);
        orderLineEntity.setOrderId(orderId);
        orderLineEntity.setProductId(productId);
        orderLineEntity.setProductName(product.getName());
        orderLineEntity.setProductSnapshotId(latestProductSnapshotId);
        orderLineEntity.setProductCampusId(product.getCampusId());
        orderLineEntity.setProductCampusName(product.getCampusName());
        orderLineEntity.setProductSubjectId(product.getSubjectId());
        orderLineEntity.setProductSubjectName(product.getSubjectName());
        orderLineEntity.setProductModelId(product.getModelId());
        orderLineEntity.setProductModelName(product.getModelName());
        orderLineEntity.setProductClassTypeId(product.getClassTypeId());
        orderLineEntity.setProductClassTypeName(product.getClassTypeName());
        orderLineEntity.setListPrice(product.getListPrice());
        orderLineEntity.setQuantity(line.getQuantity().byteValue());

        // 从产品定价规则获取当前产品定价，并校验前端传入的价格是否匹配。XXX 因价格同步存在延迟，如果价格修改较为频繁，可能下单时该处校验出问题的情况较多。
        ProductPriceRule priceRule = productPriceRuleService.getCurrentPriceRuleByProduct(productId);
        Assert.notNull(priceRule, BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品[%s，%s]价格异常：找不到其定价规则！", productId, product.getName()));
        // Integer price = convertYuanToCent(line.getPrice());
        Integer price = PaymentTypeEum.LASTPAYMENT.name().equalsIgnoreCase(typeDTO.getCode()) ? priceRule.getPostpaidPrice() : priceRule.getPrice();
        log.info("[创建订单接口] 产品ID: {}, 支付方式CODE: {} , 产品RULE价格: {}, 表单行价格:{} ", productId, typeDTO.getCode(), price, line.getPrice());
        Assert.isTrue(
                Objects.isNull(isValidatePrice)
                || !isValidatePrice
                || price.equals(line.getPrice()), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品[%s，%s]价格不正确，可能价格已更新，请重新选择产品后提交。", productId, product.getName()));
        orderLineEntity.setPrice(priceRule.getPrice());
        log.info("[创建订单接口] 验证产品价格，isValidatePrice：{}, 订单价格：{}, 产品规则价格：{}" , isValidatePrice , line.getPrice() ,price);
        log.info("[创建订单接口] 订单行数据: {}", JSON.toJSONString(line));
        // 校验前端传入的订单金额计算逻辑是否正确
        Integer originalAmount = line.getPrice() * line.getQuantity();
        Assert.isTrue(originalAmount.equals(line.getOriginalAmount()), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品[%s，%s]购买金额错误，请重新选择产品后提交。", productId, product.getName()));
        // 校验前端传入的应付金额计算逻辑是否正确
        if (!OrderSourceEnum.REPEAT.name().equalsIgnoreCase(orderSource)) {
            Integer amountAue = line.getOriginalAmount() - line.getDiscountAmount() - line.getAlreadyLearnFee();
            Assert.isTrue(amountAue.equals(line.getAmountDue()), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "产品[%s，%s]购买金额错误，请重新选择产品后提交。", productId, product.getName()));
        }
        orderLineEntity.setOriginalAmount(line.getOriginalAmount());
        orderLineEntity.setDiscountAmount(line.getDiscountAmount());
        orderLineEntity.setAmountDue(line.getAmountDue());
        orderLineEntity.setFirstPayment(line.getFirstPayment());
        return orderLineEntity;
    }

    /**
     * 获取需要持久化的优惠项列表
     *
     * @param orderId 订单头ID
     * @param lines   订单行
     * @param order   表单订单头实体
     * @return 订单优惠表实体列表
     */
    private List<OrderDiscount> wrapOrderDiscountEntityList(String orderId, @NotNull List<OrderLine> lines, NewOrderDTO order) {
        List<OrderDiscount> list = new ArrayList<>();
        if (Objects.isNull(order) || (CollectionUtils.isEmpty(order.getDiscounts()) && CollectionUtils.isEmpty(order.getPersonalDiscounts()))) {
            return list;
        }
        List<NewOrderDiscountDTO> allDiscount = new ArrayList<>();
        if (!CollectionUtils.isEmpty(order.getDiscounts())) {
            allDiscount.addAll(order.getDiscounts());
        }
        if (!CollectionUtils.isEmpty(order.getPersonalDiscounts())) {
            allDiscount.addAll(order.getPersonalDiscounts());
        }
        for (NewOrderDiscountDTO discountDTO : allDiscount) {
            ProductDTO product = productApi.findProductByIdOrBizNo(StringUtils.hasText(discountDTO.getProductId()) ? Long.valueOf(discountDTO.getProductId()) : null, discountDTO.getProductBizNo());
            Assert.notNull(product, BizExceptionProducer.badRequest(BizErrorBodyEnum.NOT_FOUND.name(), "产品ID[%s]或产品业务编码[%s]不正确，找不到对应的产品。", discountDTO.getProductId(), discountDTO.getProductBizNo()));
            discountDTO.setProductId(String.valueOf(product.getId()));

        }
        Map<String, List<NewOrderDiscountDTO>> groupByProductMap = allDiscount.stream().peek(item -> item.setProductId(StringUtils.hasText(item.getProductId()) ? item.getProductId() : "0")).collect(Collectors.groupingBy(NewOrderDiscountDTO::getProductId, Collectors.mapping(Function.identity(), Collectors.toList())));
        // 处理表单优惠项 productionId 为空的情况 , 这种优惠项绑定订单头 ,且影响订单头的优惠总计
        List<NewOrderDiscountDTO> forAll = groupByProductMap.get("0");
        if (CollectionUtil.isNotEmpty(forAll)) {
            for (NewOrderDiscountDTO discountDTO : forAll) {
                list.add(this.wrapDiscountEntity(orderId, discountDTO, order.getCreatedBy()));
            }
        }
        // 订单行包含的 产品ID , 一般情况下,  每个productionId 对应一个订单行
        final List<Long> productIds = lines.stream().map(OrderLine::getProductId).filter(CustomerValidator.distinctByKey(Long::longValue)).toList();
        // 检查是否有不属于 订单行的 productId 优惠项
        allDiscount.forEach(item -> Assert.isTrue(Objects.isNull(item.getProductId()) || productIds.contains(Long.valueOf(item.getProductId())), BizExceptionProducer.badRequest(BizErrorBodyEnum.NOT_FOUND.name(), "订单行不存在该产品ID:%s", item.getProductId())));
        /*
          1、遍历分组集合 productionId
          2、根据 productionId 匹配订单行,  这里考虑了一种特殊情况,  如表单一订单头出现了2个或2个以上 相同productionId 的订单行 ,所以基于productionId 匹配的订单行做了遍历
          3、生成需要持久化的订单优惠表实体(绑定订单行ID)
         */
        for (String productId : groupByProductMap.keySet()) {
            List<OrderLine> orderLineList = lines.stream().filter(item -> String.valueOf(item.getProductId()).equals(productId)).toList();
            if (CollectionUtil.isNotEmpty(orderLineList)) {
                for (OrderLine orderLine : orderLineList) {
                    for (NewOrderDiscountDTO discountDTO : groupByProductMap.get(productId)) {
                        OrderDiscount discountEntity = this.wrapDiscountEntity(orderId, discountDTO, order.getCreatedBy());
                        discountEntity.setOrderLineId(orderLine.getId());
                        list.add(discountEntity);
                    }
                }
            }
        }
        return list;
    }

    private OrderDiscount wrapDiscountEntity(String orderId, NewOrderDiscountDTO discountDTO, String operationEmail) {
        String discountType;
        String discountName;
        String description;
        if (StringUtils.hasText(discountDTO.getJournalDiscountLineId())) {
            JournalDiscountApiResDTO discountRule = journalDiscountApi.queryByLineId(JournalDiscountApiReqDTO.buildForQueryByLineId(discountDTO.getJournalDiscountLineId()));
            Assert.notNull(discountRule, BizExceptionProducer.produce(OrderErrorBodyEnum.ORDER_JOURNAL_DISCOUNT_RULE_NOT_FOUND, discountDTO.getJournalDiscountLineId()));
            discountType = discountRule.getDiscountType();
            discountName = discountRule.getDiscountName();
            description = StringUtils.hasText(discountRule.getRemark()) ? discountRule.getRemark() : "";
        } else {
            DiscountSummaryDTO discountRule = discountApi.getSummaryById(discountDTO.getDiscountId());
            Assert.notNull(discountRule, BizExceptionProducer.produce(OrderErrorBodyEnum.ORDER_DISCOUNT_RULE_NOT_FOUND, discountDTO.getDiscountId(), discountDTO.getDescription()));
            Assert.isTrue(discountRule.getState() == 1, BizExceptionProducer.produce(OrderErrorBodyEnum.ORDER_DISCOUNT_RULE_UNAVAILABLE, discountDTO.getDiscountId(), discountDTO.getDescription()));
            discountType = DiscountTypeEnum.getByValue(discountRule.getType()).toString();
            discountName = discountRule.getName();
            description = discountRule.getDescription();
        }
        OrderDiscount discountEntity = new OrderDiscount();
        discountEntity.setOrderId(orderId);
        discountEntity.setDiscountId(discountDTO.getDiscountId());
        discountEntity.setDiscountName(discountName);
        discountEntity.setDiscountType(discountType);
        discountEntity.setDiscountAmount(discountDTO.getDiscountAmount());
        discountEntity.setSort(discountDTO.getSort());
        discountEntity.setDescription(description);
        discountEntity.setCreatedBy(operationEmail);
        discountEntity.setModifiedBy(operationEmail);
        if (StringUtils.hasText(discountDTO.getJournalDiscountLineId())) {
            discountEntity.setDiscountScopeType(DiscountScopeTypeEnum.PERSONAL.name());
            discountEntity.setJournalDiscountLineId(discountDTO.getJournalDiscountLineId());
        } else {
            discountEntity.setDiscountScopeType(DiscountScopeTypeEnum.NORMAL.name());
        }
        return discountEntity;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> paymentSuccess(@NonNull OrderPaymentResultDTO paymentResult) {
        log.debug("paymentSuccess - BEGIN，参数：{}", paymentResult);

        String orderNo = paymentResult.getOrderNo();
        String bizOrderNo = paymentResult.getBizOrderNo();
        List<OrderPaymentFlowDTO> flows = paymentResult.getFlows();

        Assert.isTrue(StringUtils.hasText(orderNo) || StringUtils.hasText(bizOrderNo), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "订单号或业务订单号必选其一！"));
        Assert.notEmpty(flows, BizExceptionProducer.badRequest(BizErrorBodyEnum.NOT_FOUND.name(), "支付流水不能为空！"));

        final Order order = orderMapper.selectOne(new WrapperUtil<Order>().limitLaseOne("created_datetime").eq(StringUtils.hasText(orderNo), Order::getOrderNo, orderNo).eq(StringUtils.hasText(bizOrderNo), Order::getBizOrderNo, bizOrderNo));
        Assert.notNull(order, BizExceptionProducer.badRequest(BizErrorBodyEnum.NOT_FOUND.name(), "找不到对应的订单，订单号：%s", StringUtils.hasText(bizOrderNo) ? bizOrderNo : orderNo));

        List<String> flowNos = new ArrayList<>();
        List<PaymentReceipt> receipts = new ArrayList<>();
        Integer paidAmount = 0;
        LocalDateTime lastPaymentDate = null;

        List<OrderPaymentFlow> flowEntities = new ArrayList<>();
        for (OrderPaymentFlowDTO flow : flows) {
            OrderPaymentFlow flowEntity = new OrderPaymentFlow();
            String flowNo = this.bizNumberService.next(BizNumberTypeEnum.ORDER_PAYMENT_FLOW);
            flowNos.add(flowNo);

            BeanUtils.copyProperties(flow, flowEntity);
            flowEntity.setId(generateSimpleUUID());
            flowEntity.setOrderId(order.getId());
            flowEntity.setNo(flowNo);
            flowEntity.setJournalType(flow.getJournalType());
            flowEntity.setJournalNo(flow.getJournalNo());
            flowEntity.setAmount(flow.getAmount());
            flowEntity.setCharge(flow.getCharge());
            flowEntities.add(flowEntity);

            // 计算合计值
            paidAmount += flow.getAmount();

            Assert.notNull(flow.getBizDate(), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "支付时间不能为空！"));

            if (lastPaymentDate == null || flow.getBizDate().isAfter(lastPaymentDate)) {
                lastPaymentDate = flow.getBizDate();
            }
            // 添加支付收据
            PaymentReceipt receipt = new PaymentReceipt();
            receipt.setOrderId(order.getId());
            receipt.setPaymentFlowId(flowEntity.getId());
            receipt.setNo(this.bizNumberService.next(BizNumberTypeEnum.ORDER_PAYMENT_RECEIPT));
            receipt.setAmount(flowEntity.getAmount());
            receipt.setState(PaymentReceiptStateEnum.VALID.toString());
            receipts.add(receipt);
        }
        orderPaymentFlowService.saveBatch(flowEntities);
        paymentReceiptService.saveBatch(receipts);

        // 总已付金额累加之前已支付金额
        paidAmount += order.getPaidAmount();
        // 更新订单头
        OrderStateEnum stateEnum = calcOrderPaymentState(order.getAmountDue(), paidAmount);
        orderMapper.update(null, new UpdateWrapper<Order>().set("paid_amount", paidAmount).set("payment_datetime", lastPaymentDate).set("state", stateEnum.name()).eq("id", order.getId()));

        // 添加订单事件
        orderEventService.save(wrapOrderEventEntityByPaymentSuccess(order.getId(), order.getAmountDue(), paidAmount));
        // 投递支付消息
        for (int i = 0; i < flowEntities.size(); i++) {
            OrderPaymentFlow flow = flowEntities.get(i);
            order.setPaidAmount(order.getPaidAmount() + flow.getAmount());
            order.setState(calcOrderPaymentState(order.getAmountDue(), order.getPaidAmount()).name());
            this.sendMessageAfterPaid(order, flow);
        }
        return flowNos;
    }

    @Override
    public void addPaymentFlow(@NonNull PaymentFlowDTO flowDTO) {
        try {
            log.info("添加流水: {}", flowDTO);
            Assert.notEmpty(flowDTO.getOrderId(), BizExceptionProducer.produce(NemsErrorBodyEnum.PARAM_EMPTY, "订单ID"));
            Order order = orderMapper.selectById(flowDTO.getOrderId());
            Assert.notNull(order, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "订单ID：" + flowDTO.getOrderId()));
            OrderPaymentFlow flowEntity = new OrderPaymentFlow();
            String flowNo = this.bizNumberService.next(BizNumberTypeEnum.ORDER_PAYMENT_FLOW);
            BeanUtils.copyProperties(flowDTO, flowEntity);
            flowEntity.setId(generateSimpleUUID());
            flowEntity.setOrderId(order.getId());
            flowEntity.setNo(flowNo);
            flowEntity.setJournalType(flowDTO.getJournalType());
            flowEntity.setJournalNo(flowDTO.getJournalNo());
            flowEntity.setAmount(flowDTO.getAmount());
            flowEntity.setCharge(flowDTO.getCharge());
            // 计算合计值
            int paidAmount = flowDTO.getAmount() + order.getPaidAmount();
            Assert.notNull(flowDTO.getBizDate(), BizExceptionProducer.badRequest(BizErrorBodyEnum.PARAMETER_ERROR.name(), "支付时间不能为空！"));
            // 添加支付收据
            PaymentReceipt receipt = new PaymentReceipt();
            receipt.setOrderId(order.getId());
            receipt.setPaymentFlowId(flowEntity.getId());
            receipt.setNo(this.bizNumberService.next(BizNumberTypeEnum.ORDER_PAYMENT_RECEIPT));
            receipt.setAmount(flowEntity.getAmount());
            receipt.setState(PaymentReceiptStateEnum.VALID.toString());
            log.info("保存流水: {} {}", flowEntity.getOrderId(), flowEntity.getNo());
            if (!StringUtils.hasText(flowEntity.getCreatedBy())) {
                flowEntity.setCreatedBy(AccountHolder.getRequiredAccount());
            }
            orderPaymentFlowService.save(flowEntity);
            log.info("添加流水收据: {} {}", receipt.getOrderId(), receipt.getPaymentFlowId());
            if (!StringUtils.hasText(receipt.getCreatedBy())) {
                receipt.setCreatedBy(AccountHolder.getRequiredAccount());
            }
            paymentReceiptService.save(receipt);
            // 总已付金额累加之前已支付金额
            paidAmount += order.getPaidAmount();
            // 更新订单头
            // 2024-02-01  维护订单 "payment_time" 取值流水 "biz_date"改成 流水 "created_datetime"
            // 转班算课耗生成流水， "biz_date" 为进班时间
            OrderStateEnum stateEnum = calcOrderPaymentState(order.getAmountDue(), paidAmount);
            orderMapper.update(null,
                    new UpdateWrapper<Order>()
                            .set("paid_amount", order.getPaidAmount() + flowDTO.getAmount())
                            .set("payment_datetime",  flowDTO.getBizDate())
                            .set("state", stateEnum.name())
                            .eq("id", order.getId()));

            // 添加订单事件
            orderEventService.save(wrapOrderEventEntityByPaymentSuccess(order.getId(), order.getAmountDue(), paidAmount));
            // 投递订单支付消息
            order.setPaidAmount(paidAmount);
            order.setState(calcOrderPaymentState(order.getAmountDue(), order.getPaidAmount()).name());
            this.sendMessageAfterPaid(order, flowEntity, flowDTO.getStudentRecordFundFlowDTO());
        } catch (Throwable e) {
            log.error(e.getMessage(), e);
            throw e;
        }
    }

    @Override
    public OrderMode findByStudentRecordId(String studentRecordId) {
        Order order = orderMapper.findByStudentRecordId(studentRecordId);
        if (Objects.nonNull(order)) {
            return CustomBeanUtil.copyProperties(order, new OrderMode());
        }
        return null;
    }

    private void sendMessageAfterPaid(Order order, OrderPaymentFlow flow) {
        sendMessageAfterPaid(order, flow, null);
    }

    /**
     * 支付回调投递消息
     *
     * @param order 主订单
     * @param flow  支付流水
     **/
    private void sendMessageAfterPaid(Order order, OrderPaymentFlow flow, StudentRecordFundFlowDTO studentRecordFundFlowDTO) {
        OrderPaidMessageDTO paidMessageDTO = new OrderPaidMessageDTO();
        paidMessageDTO.setOrderId(order.getId());
        paidMessageDTO.setOrderNo(order.getOrderNo());
        // 订单行ID
        List<String> orderLines = orderLineService.listObjs(new LambdaQueryWrapper<OrderLine>().select(OrderLine::getId).eq(OrderLine::getOrderId, order.getId()).orderByAsc(OrderLine::getCreatedDatetime), Object::toString);
        log.info("【订单支付投递消息】字段订单行数据：{}", JSON.toJSONString(orderLines));
        paidMessageDTO.setOrderLineIds(orderLines);
        paidMessageDTO.setBizOrderNo(order.getBizOrderNo());
        paidMessageDTO.setOrderPaidAmount(order.getPaidAmount());
        paidMessageDTO.setOrderAmountDue(order.getAmountDue());
        paidMessageDTO.setBusinessLineId(order.getBusinessLineId());
        paidMessageDTO.setTenantId(order.getTenantId());
        paidMessageDTO.setSubtenantId(order.getSubtenantId());
        paidMessageDTO.setPaidFlag(OrderStateEnum.PAID.name().equalsIgnoreCase(order.getState()));
        paidMessageDTO.setCustomerId(order.getCustomerId());
        // 流水号
        paidMessageDTO.setFlowNo(flow.getNo());
        // 本次支付金额
        paidMessageDTO.setFlowAmount(flow.getAmount());
        // 支付渠道帐号编号
        paidMessageDTO.setPaymentAccountCode(flow.getPaymentAccountCode());
        // 支付渠道帐号名称
        paidMessageDTO.setPaymentAccountName(flow.getPaymentAccountName());
        // 支付方式编号
        paidMessageDTO.setPaymentMethodCode(flow.getPaymentMethodCode());
        // 支付方式名称
        paidMessageDTO.setPaymentMethodName(flow.getPaymentMethodName());
        // 业务操作时间
        paidMessageDTO.setBizDate(flow.getBizDate());
        // 支付类型
        paidMessageDTO.setJournalType(flow.getJournalType());
        paidMessageDTO.setSource(order.getSource());
        paidMessageDTO.setStudentRecordFundFlowDTO(studentRecordFundFlowDTO);
        paidMessageDTO.setCharge(flow.getCharge());
        paidMessageDTO.setChargeRate(flow.getChargeRate());
        paidMessageDTO.setRemark(flow.getRemark());
        AccountHolder.getAccountInfo().ifPresentOrElse(accountInfo -> paidMessageDTO.setOperatorEmail(accountInfo.getEmail()),
                () -> paidMessageDTO.setOperatorEmail(order.getCreatedBy()));
        MessageManager.asyncSendOrderly(rocketMQTemplate, rocketMQProperties.getConsumer().getTopic() + ":" + MessageTagConstant.TAG_NEMS_ORDER_PAID, JSON.toJSONString(paidMessageDTO), order.getId());
    }

    private OrderStateEnum calcOrderPaymentState(@NonNull Integer amountDue, @NonNull Integer paidAmount) {
        return paidAmount.compareTo(amountDue) != -1 ? OrderStateEnum.PAID : OrderStateEnum.PAYING;
    }

    private OrderEvent wrapOrderEventEntityByPaymentSuccess(String orderId, @NonNull Integer amountDue, @NonNull Integer paidAmount) {
        OrderEvent eventEntity = new OrderEvent();
        eventEntity.setOrderId(orderId);
        eventEntity.setType(OrderEventTypeEnum.PARTIAL_PAYMENT.getName());

        if (paidAmount.compareTo(amountDue) == 0) {
            eventEntity.setType(OrderEventTypeEnum.COMPLETED_PAYMENT.getName());
        } else if (paidAmount.compareTo(amountDue) > 0) {
            eventEntity.setType(OrderEventTypeEnum.OVERFLOWED_PAYMENT.getName());
        }

        eventEntity.setDescription("订单支付");
        return eventEntity;
    }

    @Override
    public cn.itcast.ic.common.bean.Page<OrderDO> list(Integer pageNumber, Integer pageSize, OrderDO orderDO) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<OrderDO> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>();
        page.setCurrent(pageNumber);
        page.setSize(pageSize);
        if (orderDO.getProductSubjectIds() != null && orderDO.getProductSubjectIds().size() != 0) {
            orderDO.setProductSubjectId("'" + Joiner.on("','").join(orderDO.getProductSubjectIds()) + "'");
        }
        if (orderDO.getProductCampusIds() != null && orderDO.getProductCampusIds().size() != 0) {
            orderDO.setProductCampusId("'" + Joiner.on("','").join(orderDO.getProductCampusIds()) + "'");
        }
        if (orderDO.getSources() != null && orderDO.getSources().size() != 0) {
            orderDO.setSource("'" + Joiner.on("','").join(orderDO.getSources()) + "'");
        }
        if (!AccountHolder.hasEmployeeGroups()) {
            return Page.of(pageNumber, pageSize, 0, null);
        }
        orderDO.setEmployeeGroups(AccountHolder.getEmployeeGroups());
        IPage<OrderDO> pageList = orderMapper.findPageList(page, orderDO);
        List<OrderDO> list = CollectionUtil.isEmpty(pageList.getRecords()) ? null : pageList.getRecords().stream().map(item -> {
            item.setJournalId(this.getJournalById(item.getId(), item.getSource()));
            return item;
        }).toList();
        return cn.itcast.ic.common.bean.Page.of(pageNumber, pageSize, Long.valueOf(pageList.getTotal()).intValue(), list);
    }

    @Override
    public Map<String, Object> orderInfo(String orderId) {
        Map<String, Object> map = orderMapper.orderInfo(orderId);
        if (map == null) {
            return null;
        }
        String orderSource = (String) map.get("source");
        map.put("journal_id", getJournalById(orderId, orderSource));
        map.put("student_id", orderMapper.getStudentIdByOrderId(orderId));
        return map;
    }


    @Override
    public List<Map<String, Object>> listOrderNoByCustomerName(String customerId) {
        return orderMapper.listOrderNoByCustomerName(customerId);
    }

    private String generateSimpleUUID() {
        return UUID.fastUUID().toString(true);
    }

    @Override
    public Order getByOrderNo(String orderNo) {
        return orderMapper.selectByNo(orderNo);
    }

    @Override
    public Order getByOrderBizNo(String orderBizNo) {
        return orderMapper.selectOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getBizOrderNo, orderBizNo)
                .eq(Order::getDeletedFlag, Boolean.FALSE)
                .notIn(Order::getState, "CANCELED"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancel(String id) {
        Order order = orderMapper.selectById(id);
        Assert.notNull(order, "订单不存在");
        Assert.isTrue(!OrderSourceEnum.TRANSFER.name().equalsIgnoreCase(order.getSource()), BizExceptionProducer.produce(OrderErrorBodyEnum.ORDER_TRANSFER_NO_ALLOWED));
        // 订单状态：待支付|PENDING，部分支付|PAYING，完成支付|PAID，已取消|CANCELED
        Assert.isTrue(order.getState().equals(OrderStateEnum.PENDING.name()), BizExceptionProducer.badRequest("当前订单已支付，无法取消！"));

        // 1.e2_order表 state字段 订单状态：待支付|PENDING，改为，已取消|CANCELED
        order.setState(OrderStateEnum.CANCELED.name());
        boolean b = orderMapper.updateById(order) == 1;

        // 2.e2_order_event 销售订单-事件/日志
        // 事件类型：创建订单、部分支付、完成支付、订单取消、订单退费、开取发票、开取收据。注意：直接填中文即可
        orderEventService.create(id, OrderTypeEnum.ORDER_CANCEL.getName(), OrderTypeEnum.ORDER_CANCEL.getName());
        // 投递订单取消消息
        this.sendMessageAfterCanceled(order);
        return b;
    }


    public void sendMessageAfterCanceled(Order order) {
        OrderCanceledMessageDTO canceledMessageDTO = new OrderCanceledMessageDTO();
        canceledMessageDTO.setBizDate(LocalDateTime.now());
        canceledMessageDTO.setOrderId(order.getId());
        // 订单行ID
        List<String> orderLines = orderLineService.listObjs(new LambdaQueryWrapper<OrderLine>().select(OrderLine::getId).eq(OrderLine::getOrderId, order.getId()).orderByAsc(OrderLine::getCreatedDatetime), Object::toString);
        // 应付金额
        canceledMessageDTO.setAmountDue(order.getAmountDue());
        canceledMessageDTO.setOrderLineIds(orderLines);
        canceledMessageDTO.setOrderNo(order.getOrderNo());
        canceledMessageDTO.setBizOrderNo(order.getBizOrderNo());
        canceledMessageDTO.setCustomerId(order.getCustomerId());
        canceledMessageDTO.setBusinessLineId(order.getBusinessLineId());
        canceledMessageDTO.setTenantId(order.getTenantId());
        canceledMessageDTO.setSubtenantId(order.getSubtenantId());
        canceledMessageDTO.setSource(order.getSource());
        AccountHolder.getAccountInfo().ifPresentOrElse(accountInfo -> canceledMessageDTO.setOperatorEmail(accountInfo.getEmail()),
                () -> canceledMessageDTO.setOperatorEmail(order.getCreatedBy()));
        MessageManager.asyncSendOrderly(rocketMQTemplate, rocketMQProperties.getConsumer().getTopic() + ":" + MessageTagConstant.TAG_NEMS_ORDER_CANCELED, JSON.toJSONString(canceledMessageDTO), order.getCustomerId());
        final String source = order.getSource();
        if (OrderSourceEnum.NEW.name().contentEquals(source)) {
            final StudentClazzIdDO studentClazzId = this.orderMapper.findStudentClazzId(order.getId());
            if(studentClazzId != null) {
                log.info("取消报名系统订单, studentId:{}, clazzId:{}", studentClazzId.getStudentId(), studentClazzId.getClazzId());
                this.enrolManager.cancelOrder(studentClazzId.getStudentId(), studentClazzId.getClazzId());
            }
        }
    }

    @Override
    public String getJournalById(String orderId, String orderSource) {
        if (!StringUtils.hasText(orderId) || !StringUtils.hasText(orderSource)) {
            return null;
        }
        // 订单来源：新报名｜NEW、升级｜RENEWAL、转班｜TRANSFER、重读/留级｜REPEAT
        if (orderSource.equals(OrderSourceEnum.RENEWAL.name())) {
            JournalRenewalOrderRelationship renewalOrderRelationship = journalRenewalOrderRelationshipService.getByOrderId(orderId);
            return Objects.nonNull(renewalOrderRelationship) ? renewalOrderRelationship.getJournalRenewalId() : null;
        } else if (orderSource.equals(OrderSourceEnum.TRANSFER.name())) {
            JournalTransferOrderRelationship transferOrderRelationship = journalTransferOrderRelationshipService.getByOrderId(orderId);
            return Objects.nonNull(transferOrderRelationship) ? transferOrderRelationship.getJournalTransferId() : null;
        } else if (orderSource.equals(OrderSourceEnum.REPEAT.name())) {
            // 重读/留级
            return getJournalRepeatByOrderId(orderId);
        }
        return null;
    }

    @Override
    public String getJournalLineIdByOrderId(String orderId, OrderSourceEnum orderSourceEnum) {
        if (!StringUtils.hasText(orderId) || Objects.isNull(orderSourceEnum)) {
            return null;
        }
        switch (orderSourceEnum) {
            case RENEWAL -> {
                JournalRenewalOrderRelationship renewalOrderRelationship = journalRenewalOrderRelationshipService.getByOrderId(orderId);
                return Objects.nonNull(renewalOrderRelationship) ? renewalOrderRelationship.getJournalRenewalLineId() : null;
            }
            case TRANSFER -> {
                JournalTransferOrderRelationship transferOrderRelationship = journalTransferOrderRelationshipService.getByOrderId(orderId);
                return Objects.nonNull(transferOrderRelationship) ? transferOrderRelationship.getJournalTransferLineId() : null;
            }
            default -> {
                return null;
            }
        }
    }




    private String getJournalRepeatByOrderId(String orderId) {
        JournalRepeat journalRepeat = journalRepeatMapper.selectOne(new LambdaQueryWrapper<JournalRepeat>().eq(JournalRepeat::getOrderId, orderId).eq(JournalRepeat::getDeletedFlag, false));
        if (journalRepeat == null) {
            return null;
        }
        return journalRepeat.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancelByBizNo(String bizOrderNo) {
        Order order = this.getByOrderBizNo(bizOrderNo);
        Assert.notNull(order, BizExceptionProducer.badRequest("业务订单号错误，找不到对应的单据！业务单据号：%s", bizOrderNo));
        return this.cancel(order.getId());
    }

    @Override
    public OrderMode findByBizOrderNo(@NonNull String bizOrderNo) {
        Order order = orderMapper.selectByBizOrderNo(bizOrderNo);
        if (Objects.nonNull(order)) {
            return CustomBeanUtil.copyProperties(order, new OrderMode());
        }
        return null;
    }

    @Override
    public List<OrderModeDiscountDTO> findOrderDiscounts(String orderId) {
        return this.orderDiscountService.findDiscounts(orderId);
    }

    @Override
    public List<OrderModeLineDTO> findOrderLines(String orderId) {
        final List<Map<String, Object>> rows = this.orderLineService.listOrderLine(orderId);
        List<OrderModeLineDTO> result = new ArrayList<>();
        if (rows != null && !rows.isEmpty()) {
            rows.forEach(row -> {
                OrderModeLineDTO line = new OrderModeLineDTO();
                line.setId(row.get("id").toString());
                line.setOrderId(row.get("order_id").toString());
                line.setProductId((Long) row.get("product_id"));
                line.setProductName(row.get("product_name").toString());
                result.add(line);
            });
        }
        return result;
    }

    @Override
    public OrderMode findById(String orderId) {
        Order order = orderMapper.selectById(orderId);
        if (Objects.nonNull(order)) {
            return CustomBeanUtil.copyProperties(order, new OrderMode());
        }
        return null;
    }
}
